package com.mxb.factory;

import com.alibaba.druid.util.StringUtils;
import com.mxb.annotation.Autowired;
import com.mxb.annotation.Service;
import com.mxb.annotation.Transactional;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 注解解析工厂类
 *
 * @Author: MaXiaobing
 * @Date: 2021/4/17 19:42
 */
public class AnnotationBeanFactory {

    private static String basePackage = "com.mxb";
    private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();

    // 解析context.xml读取自动扫描注解类的包路径参数
    static {
        InputStream resourceStream = AnnotationBeanFactory.class.getClassLoader().getResourceAsStream("context.xml");
        SAXReader saxReader = new SAXReader();
        Document document;
        try {
            document = saxReader.read(resourceStream);
            Element rootElement = document.getRootElement();
            List<Element> nodes = rootElement.selectNodes("//component-scan");
            basePackage = nodes.get(0).attribute("base-package").getValue();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public AnnotationBeanFactory() {
        try {
            initBeans();
            initDependency();
            initTransactional();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化包含注解的bean对象
     * @throws Exception
     */
    public void initBeans() throws Exception {
        // 使用反射工具Reflections扫描basePackage路径下的所有类
        Reflections reflections = new Reflections(basePackage);
        Set<Class<?>> serviceClazz = reflections.getTypesAnnotatedWith(Service.class);
        for (Class<?> clazz : serviceClazz) {
            Object object = clazz.getDeclaredConstructor().newInstance();
            Class[] interfaces = clazz.getInterfaces();
            // 处理Service注解
            Service annotation = clazz.getAnnotation(Service.class);
            String key = "";
            // 判断注解是否有value值，有则优先取value值作为beanMap的key
            if (!StringUtils.isEmpty(annotation.value())) {
                key = annotation.value();
            } else {
                // 判断当前对象是否是接口实现类
                if (interfaces != null && interfaces.length > 0) {
                    // 是则优先取接口名，驼峰命名处理后的作为beanMap的key
                    String[] split = interfaces[0].getName().split("\\.");
                    key = firstLowerCase(split[split.length - 1]);
                } else {
                    // 否则取当前对象的类名，驼峰命名处理后作为beanMap的key
                    String[] split = clazz.getName().split("\\.");
                    key = firstLowerCase(split[split.length - 1]);
                }
            }
            beanMap.put(key, object);
        }
    }

    /**
     * 维护bean对象依赖关系
     *
     * @throws Exception
     */
    private void initDependency() throws Exception {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object object = entry.getValue();
            Class<? extends Object> classInfo = object.getClass();
            Field[] declaredFields = classInfo.getDeclaredFields();
            for (Field field : declaredFields) {
                // 处理Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    String[] names = field.getType().getName().split("\\.");
                    String beanId = firstLowerCase(names[names.length - 1]);
                    // 通过beanMap获取依赖对象
                    Field declaredField = classInfo.getDeclaredField(beanId);
                    declaredField.setAccessible(true);
                    Object dependObj = beanMap.get(beanId);
                    // 通过属性注入依赖对象
                    declaredField.set(object, dependObj);
                }
            }
            beanMap.put(entry.getKey(), object);
        }
    }

    /**
     * 加入声明式事务控制
     * @throws Exception
     */
    private void initTransactional() throws Exception {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object object = entry.getValue();
            Class<? extends Object> classInfo = object.getClass();
            // 处理Transactional注解
            if (classInfo.isAnnotationPresent(Transactional.class)) {
                ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                Class[] interfaces = classInfo.getInterfaces();
                // 根据对象是否实现接口，自动选择代理方式
                object = (interfaces != null && interfaces.length > 0) ?
                        proxyFactory.getJdkProxy(object) : proxyFactory.getCglibProxy(object);
            }
            beanMap.put(entry.getKey(), object);
        }
    }

    /**
     * 根据beanId获取bean对象
     * @param beanId
     * @return
     * @throws Exception
     */
    public static Object getBean(String beanId) throws Exception {
        if (StringUtils.isEmpty(beanId)) {
            throw new Exception("bean Id 不能为空");
        }
        Object object = beanMap.get(beanId);
        if (object == null) {
            throw new Exception("Class not found");
        }
        return object;
    }

    /**
     * 首字母小写转换
     * @param str
     * @return
     */
    public static String firstLowerCase(String str) {
        if (Character.isLowerCase(str.charAt(0)))
            return str;
        else
            return Character.toLowerCase(str.charAt(0)) + str.substring(1);
    }

}
